/******************************************************************************
 * Copyright 2022 The Airos Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/
#include <functional>
#include <memory>
#include <thread>

#include <glog/logging.h>

#include "air_link.h"
#include "framework/flags.h"
#include "framework/msg.h"
#include "framework/mod_manager.h"
#include "framework/app.h"
#include "components/air_api_service/func_handle.h"
#ifdef USE_FAKE_OBU
#include "components/fake_obu_service/fake_obu_service.h"
#endif
#ifdef USE_SCENE_SERVICE
#include "components/scene_service/scene_service.h"
#endif
#ifdef USE_PC5_SERVICE
#include "components/pc5_service/pc5_service.h"
#endif

namespace air {
namespace link {

bool AirLink::Init() {
  app_ = std::make_shared<air::link::App>();
  if (false == app_->Init()) {
    LOG(ERROR) << "Init failed.";
    return false;
  }
  auto& mod = app_->GetModManager();
#ifdef USE_FAKE_OBU
  // 注冊fake obu服务
  {
    mod->RegWorker(
        "FakeObuReceiver",
        [](const std::string&){
          return std::make_shared<air::net::FakeObuReceiver>();
        });
    auto recv_worker = mod->CreateWorkerInst("class", "FakeObuReceiver");
    if (!app_->AddWorker("FakeObuReceiver", recv_worker)) {
      LOG(ERROR) << "add fack obu receiver service failed";
      return -1;
    }
    mod->RegWorker(
        "FakeObuSender",
        [](const std::string&){
          return std::make_shared<air::net::FakeObuSender>();
        });
    auto send_worker = mod->CreateWorkerInst("class", "FakeObuSender");
    if (!app_->AddWorker("FakeObuSender", send_worker)) {
      LOG(ERROR) << "add fake obu sender service failed";
      return -1;
    }
  }
#endif
#ifdef USE_SCENE_SERVICE
  // 注册场景服务
  {
    mod->RegActor(
      "SceneService",
      [](const std::string&){
        return std::make_shared<air::net::SceneService>();
      });
    auto scene_service = mod->CreateActorInst("class", "SceneService");
    if (!app_->AddActor("SceneService", "", scene_service)) {
      LOG(ERROR) << "add scene service failed";
      return -1;
    }
  }
#endif

#ifdef USE_PC5_SERVICE
  {
    mod->RegWorker("Pc5ServiceReceiver", [](const std::string&) {
      return std::make_shared<air::net::Pc5ServiceReceiver>();
    });
    auto recv_worker = mod->CreateWorkerInst("class", "Pc5ServiceReceiver");
    if (!app_->AddWorker("Pc5ServiceReceiver", recv_worker)) {
      LOG(ERROR) << "add pc5 receiver service failed";
      return -1;
    }
    mod->RegWorker("Pc5ServiceSender", [](const std::string&) {
      return std::make_shared<air::net::Pc5ServiceSender>();
    });
    auto send_worker = mod->CreateWorkerInst("class", "Pc5ServiceSender");
    if (!app_->AddWorker("Pc5ServiceSender", send_worker)) {
      LOG(ERROR) << "add pc5 sender service failed";
      return -1;
    }
  }
#endif

  mod->RegActor("VehicleBasicInfoApi", [](const std::string&){
    return std::make_shared<air::api::VehicleBasicInfoApi>(); });
  auto actor = mod->CreateActorInst("class", "VehicleBasicInfoApi");
  app_->AddActor("VehicleBasicInfoApi", "", actor);

#ifdef LOAD_SERVICE_FROM_LIB
  app_->LoadModsFromConf(FLAGS_air_link_service_dir);
#endif
  return true;
}

bool AirLink::RegisterTrafficProtoCallBack(
  const std::function<void(const std::string&)>& traffic_pb_func) {
  auto& mod = app_->GetModManager();
  if (!mod->RegActor("TrafficProtoApi", [](const std::string&){
    return std::make_shared<air::api::TrafficProtoApi>(); })) {
    return false;
  }
  auto actor = mod->CreateActorInst("class", "TrafficProtoApi");
  std::dynamic_pointer_cast<air::api::TrafficProtoApi>(actor)
      ->RegisterCallBack(traffic_pb_func);
  app_->AddActor("TrafficProtoApi", "", actor);
  return true;
}

bool AirLink::RegisterAsnCallBack(
  const std::function<void(const std::string&)>& asn_func) {
  auto& mod = app_->GetModManager();
  if (!mod->RegActor("AsnApi", [](const std::string&){
    return std::make_shared<air::api::AsnApi>(); })) {
    return false;
  }
  auto actor = mod->CreateActorInst("class", "AsnApi");
  std::dynamic_pointer_cast<air::api::AsnApi>(actor)
      ->RegisterCallBack(asn_func);
  app_->AddActor("AsnApi", "", actor);
  return true;
}

bool AirLink::SendVehicleBasicInfo(const std::string& basic_info) {
  air::link::Msg send_msg;
  send_msg.SetData(basic_info);
  app_->SendRequest("actor.VehicleBasicInfoApi.VehicleBasicInfoApi",
      std::make_shared<Msg>(send_msg));
  return true;
}

bool AirLink::Exec() {
  exec_func_.reset(new std::thread(std::bind(&air::link::App::Exec, app_)));
  return true;
}

}  // namespace link
}  // namespace air
